Skip to content

fix: fetch plugin documentation - #26

Merged
fbartusch merged 4 commits into
snakemake:mainfrom
schnea:main
Mar 9, 2026
Merged

fix: fetch plugin documentation#26
fbartusch merged 4 commits into
snakemake:mainfrom
schnea:main

Conversation

@schnea

@schnea schnea commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

fixes #23 .

Since GH became more agressive with its rate limiting, the collection of plugin docs would fail with a HTTP status code 429: Too many requests.

This PR moves the markdown fetching logic from plain http requests to using gitPython.

Test Plan

  1. run pixi run build
  2. Open the build/index.html file and check that
    a. Plugins that provide 'intro.md' (e.g. the slurm executor plugin) have their documentation displayed
    b. Pages that do not provide their own documentation show a little warning box

Summary by CodeRabbit

  • Refactor

    • Switched plugin documentation retrieval to a Git-based cloning approach with multi-branch support, replacing the prior HTTP-based retrieval.
  • Chores

    • Added a Git-related dependency (gitpython) to support repository cloning.
  • Style

    • Cleaned up CI workflow YAML formatting and alignment; no behavioral changes.

@schnea
schnea requested a review from fbartusch March 9, 2026 15:23
@coderabbitai

coderabbitai Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@schnea has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 56 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ded77694-cea2-4efd-b251-80577a99e63b

📥 Commits

Reviewing files that changed from the base of the PR and between d66187d and 1132d5b.

📒 Files selected for processing (1)
  • source/collect_plugins.py
📝 Walkthrough

Walkthrough

Replaced HTTP-based plugin docs retrieval with a Git-based approach using GitPython; added retrieve_plugin_markdown_files() to clone repos and read docs/{section}.md. Added Git dependency in pixi.toml. Minor YAML formatting changes in CI workflow file.

Changes

Cohort / File(s) Summary
Dependencies
pixi.toml
Added Git-related dependencies: gitpython = ">=3.1.46,<4" and git = ">=2.53.0,<3".
Docs retrieval logic
source/collect_plugins.py
Added retrieve_plugin_markdown_files(repo_url: str, branches: [str], section: str) to clone repositories via GitPython and read docs/{section}.md; replaced previous in-function HTTP retrieval logic with the new git-based path; added import git.
CI workflow (formatting)
.github/workflows/deploy.yml
YAML formatting/quoting/indentation changes only (no functional changes): reflowed arrays/strings and realigned steps/permissions.

Sequence Diagram(s)

sequenceDiagram
    participant Collector as Collector (source/collect_plugins.py)
    participant Git as GitPython (remote repo)
    participant FS as Filesystem
    participant Renderer as Docs Renderer

    Collector->>Git: clone(repo_url, branch)
    Git-->>FS: write cloned files to tmpdir
    Collector->>FS: open docs/{section}.md
    FS-->>Collector: file content or None
    Collector->>Renderer: render docs when content present
    Renderer-->>Collector: rendered output
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: fetch plugin documentation' directly addresses the main change: replacing HTTP-based documentation fetching with Git-based retrieval to fix the rate-limiting issue.
Linked Issues check ✅ Passed The PR implements the core requirement from issue #23 by switching to GitPython-based cloning for reliable documentation fetching, addressing the rate-limiting problem.
Out of Scope Changes check ✅ Passed All code changes directly support the documented objectives: adding Git dependencies, implementing Git-based retrieval, and updating workflows for the new approach.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pixi.toml`:
- Line 23: The build contract is missing the system git executable required by
GitPython: update pixi.toml to declare the git runtime dependency (in addition
to gitpython) so the `git` binary is present in build environments; reference
the existing GitPython entry (`gitpython = ">=3.1.46,<4"`) and add an
appropriate `git` package declaration (matching your platform package naming
convention) so `source/collect_plugins.py` can safely call
`git.Repo.clone_from()` without an "executable not found" failure.

In `@source/collect_plugins.py`:
- Around line 12-14: There is a duplicate import of the module tempfile (import
tempfile appears twice); remove the redundant import so only a single "import
tempfile" remains (locate the duplicate import statement in collect_plugins.py
and delete the extra one to resolve the F811 duplicate-import error).
- Around line 349-358: The code creates tmpdir once then deletes it inside the
branch loop causing FileNotFoundError on subsequent attempts; fix by creating
and using a fresh temporary directory for each branch attempt (move
tempfile.mkdtemp() or use tempfile.TemporaryDirectory() inside the for branch in
branches loop), perform git.Repo.clone_from(repo_url, to_path=tmpdir, bare=True)
into that per-attempt directory, and ensure cleanup is done in a finally block
(or rely on TemporaryDirectory context) instead of unconditionally calling
shutil.rmtree(tmpdir) after each try so shutil.rmtree is only called for the
directory that was actually created.
- Line 344: The function signature for retrieve_plugin_markdown_files uses an
invalid annotation branches: [str] which is a list literal, not a type hint;
change the parameter annotation to a proper sequence type such as branches:
list[str] (or Sequence[str] if you prefer typing imports) in the
retrieve_plugin_markdown_files definition and update any related type hints or
references accordingly so static checkers and runtime semantics are correct.
- Around line 352-353: Validate and sanitize repo_url before calling
git.Repo.clone_from: parse repo_url (e.g., in the code surrounding the repo =
git.Repo.clone_from(repo_url, to_path=tmpdir, bare=True) call), verify the
scheme is exactly "https" and the hostname matches your allowed public hosts
list, and raise an exception or skip the package if validation fails; do this
check on the repo_url variable and only proceed to clone and use
repo.git.show(...) when the URL passes validation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7932a77f-b819-4856-b6a7-0c5ffff1dd96

📥 Commits

Reviewing files that changed from the base of the PR and between 44b7911 and 957fed7.

⛔ Files ignored due to path filters (1)
  • pixi.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • pixi.toml
  • source/collect_plugins.py

Comment thread pixi.toml
Comment thread source/collect_plugins.py Outdated
Comment thread source/collect_plugins.py Outdated

SECTION_MARK_ORDER = '#*=-^"~:`_+<'

def retrieve_plugin_markdown_files(repo_url: str, branches:[str], section: str):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
ns = {}
exec("def f(branches:[str]):\n    pass\n", ns)
ann = ns["f"].__annotations__["branches"]
print(ann)
print(type(ann).__name__)
PY

Repository: snakemake/snakemake-plugin-catalog

Length of output: 98


🏁 Script executed:

head -350 source/collect_plugins.py | tail -10

Repository: snakemake/snakemake-plugin-catalog

Length of output: 355


Fix the branches annotation.

branches: [str] is not a valid type annotation; it evaluates to a list literal containing the str type object, not a list type hint. Use list[str] instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/collect_plugins.py` at line 344, The function signature for
retrieve_plugin_markdown_files uses an invalid annotation branches: [str] which
is a list literal, not a type hint; change the parameter annotation to a proper
sequence type such as branches: list[str] (or Sequence[str] if you prefer typing
imports) in the retrieve_plugin_markdown_files definition and update any related
type hints or references accordingly so static checkers and runtime semantics
are correct.

Comment thread source/collect_plugins.py Outdated
Comment thread source/collect_plugins.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
source/collect_plugins.py (1)

360-360: Avoid mutable default argument.

Using a mutable list as a default argument (branches=["main", "master"]) is a Python anti-pattern. If the list were ever mutated, changes would persist across function calls.

♻️ Suggested fix
-def get_docs(repository: str | None, section: str, branches=["main", "master"]):
+def get_docs(repository: str | None, section: str, branches=None):
     if repository is None:
         return None
+    if branches is None:
+        branches = ["main", "master"]
     retrieved = retrieve_plugin_markdown_files(repository, branches, section)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/collect_plugins.py` at line 360, The function get_docs currently uses
a mutable default argument branches=["main", "master"], which can lead to state
leakage if mutated; change the signature to use branches: list[str] | None =
None (or branches=None) and inside get_docs (the get_docs function) set branches
= ["main", "master"] (or tuple("main","master")) when branches is None, ensuring
callers still get the same defaults while avoiding a mutable default; update any
type hints or callers if needed and do not mutate the default list in-place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@source/collect_plugins.py`:
- Line 360: The function get_docs currently uses a mutable default argument
branches=["main", "master"], which can lead to state leakage if mutated; change
the signature to use branches: list[str] | None = None (or branches=None) and
inside get_docs (the get_docs function) set branches = ["main", "master"] (or
tuple("main","master")) when branches is None, ensuring callers still get the
same defaults while avoiding a mutable default; update any type hints or callers
if needed and do not mutate the default list in-place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 96de2463-571f-48a4-a0af-b76a337e5242

📥 Commits

Reviewing files that changed from the base of the PR and between 957fed7 and ff1a4c1.

📒 Files selected for processing (2)
  • .github/workflows/deploy.yml
  • source/collect_plugins.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
source/collect_plugins.py (2)

343-343: ⚠️ Potential issue | 🟡 Minor

Fix the branches annotation on Line 343.

branches: [str] evaluates to a list literal, not a list type hint. Use list[str] or another real sequence type instead.

Proposed fix
-def retrieve_plugin_markdown_files(repo_url: str, branches: [str], section: str):
+def retrieve_plugin_markdown_files(repo_url: str, branches: list[str], section: str):
#!/bin/bash
python - <<'PY'
ns = {}
exec("def f(branches:[str]):\n    pass\n", ns)
bad = ns["f"].__annotations__["branches"]
print("bad:", repr(bad), type(bad).__name__)

ns = {}
exec("def f(branches:list[str]):\n    pass\n", ns)
good = ns["f"].__annotations__["branches"]
print("good:", repr(good), type(good).__name__)
PY
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/collect_plugins.py` at line 343, The type annotation for the parameter
branches in retrieve_plugin_markdown_files is using a list literal ([str])
instead of a proper type hint; change it to a real sequence type such as
list[str] (or typing.List[str] / typing.Sequence[str] for older Python versions)
and update any necessary imports (typing.List or typing.Sequence) so the
annotation is a true type hint rather than a list value.

349-351: ⚠️ Potential issue | 🟠 Major

Allowlist repository URLs before Line 351.

This helper now feeds the PyPI Repository URL straight into Repo.clone_from. That expands the trust boundary from public HTTPS fetches to whatever transports git clone accepts, including unexpected local or SSH targets. Reject anything except the HTTPS hosts you explicitly support before cloning.

Proposed fix
 def retrieve_plugin_markdown_files(repo_url: str, branches: list[str], section: str):
     """
     fetch the intro.md and further.md doc files provided by plugins
     """
+    from urllib.parse import urlparse
+
+    parsed = urlparse(repo_url)
+    if parsed.scheme != "https" or parsed.hostname not in {"github.com", "gitlab.com"}:
+        print(f"Skipping unsupported repository URL for docs fetch: {repo_url}")
+        return None
+
     docs_path = f"docs/{section}.md"
Does GitPython `Repo.clone_from` shell out to the system `git clone`, and which repository URL schemes/transports can `git clone` access by default?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/collect_plugins.py` around lines 349 - 351, Validate and allowlist
repo_url before calling git.Repo.clone_from: ensure repo_url uses HTTPS and the
hostname is one of the supported hosts (reject ssh, file, git, or other
schemes), returning or raising a clear error for disallowed URLs; perform this
check where repo_url is passed to git.Repo.clone_from (in the helper using
tempfile.TemporaryDirectory and variable repo_url), log the rejected URL and
reason, and only proceed to clone when the allowlist check passes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/collect_plugins.py`:
- Around line 349-351: The with-statement uses the TemporaryDirectory class
instead of an instance, causing a TypeError; update the context manager to
instantiate it (use tempfile.TemporaryDirectory() in the with line) so that the
context yields a usable tmpdir path for git.Repo.clone_from(repo_url,
to_path=tmpdir, bare=True) in collect_plugins.py.

---

Duplicate comments:
In `@source/collect_plugins.py`:
- Line 343: The type annotation for the parameter branches in
retrieve_plugin_markdown_files is using a list literal ([str]) instead of a
proper type hint; change it to a real sequence type such as list[str] (or
typing.List[str] / typing.Sequence[str] for older Python versions) and update
any necessary imports (typing.List or typing.Sequence) so the annotation is a
true type hint rather than a list value.
- Around line 349-351: Validate and allowlist repo_url before calling
git.Repo.clone_from: ensure repo_url uses HTTPS and the hostname is one of the
supported hosts (reject ssh, file, git, or other schemes), returning or raising
a clear error for disallowed URLs; perform this check where repo_url is passed
to git.Repo.clone_from (in the helper using tempfile.TemporaryDirectory and
variable repo_url), log the rejected URL and reason, and only proceed to clone
when the allowlist check passes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0870031d-0d42-4f97-8f9f-903f8d30380f

📥 Commits

Reviewing files that changed from the base of the PR and between ff1a4c1 and d66187d.

⛔ Files ignored due to path filters (1)
  • pixi.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • pixi.toml
  • source/collect_plugins.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • pixi.toml

Comment thread source/collect_plugins.py

@fbartusch fbartusch left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested the changes locally, the missing documentation (intro.md, further.md) is now included again.

@fbartusch
fbartusch merged commit 93de4d9 into snakemake:main Mar 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Documentation provided by plugins are not shown

2 participants